#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#pragma endregion

#include "stdafx.h"


OSHMixerPlugin::OSHMixerPlugin()
	: _filterGraph(NULL), _insideNotifyGraphChange(false), _insideStop(false), _insidePause(false), _graphIsRunning(false), //_oshAP(NULL),
	_oshSourceConnector(NULL)
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::OSHMixerPlugin() : 0x%08X\r\n", this);
}
OSHMixerPlugin::~OSHMixerPlugin()
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::~OSHMixerPlugin(0x%08X)\r\n", this);
}

HRESULT OSHMixerPlugin::FinalConstruct()
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
		"OSHMixerPlugin::FinalConstruct()\r\n");

	HRESULT hr = this->OuterQueryInterface(__uuidof(IFilterGraph), (void**)&_filterGraph);
	ATLASSERT(SUCCEEDED(hr));
	if(FAILED(hr)) return hr;

	hr = OSHSourceConnector::GetSingleton(&_oshSourceConnector);
	ATLASSERT(SUCCEEDED(hr));
	if(FAILED(hr)) return hr;

	_oshSourceConnector->Connect(this);

	// Do not AddRef this pointer to prevent circular reference count.
	_filterGraph->Release(); 

	return hr;
}

void OSHMixerPlugin::FinalRelease()
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
		"OSHMixerPlugin::FinalRelease()\r\n");
	
	//ATLASSERT(0);
	if(_oshSourceConnector)
	{
		_oshSourceConnector->Disconnect(this);
		_oshSourceConnector->Release();
		_oshSourceConnector = NULL;
	}
}

STDMETHODIMP OSHMixerPlugin::Initialize()
{
	return S_OK;
}

void OSHMixerPlugin::OnShow(OSHLibrary::IBitmap* oshBitmap)
{
	Refresh(oshBitmap);
}

void OSHMixerPlugin::OnHide()
{
	Refresh(NULL);
}

void OSHMixerPlugin::OnRefresh(OSHLibrary::IBitmap* oshBitmap)
{
	Refresh(oshBitmap);
}

void OSHMixerPlugin::Refresh(OSHLibrary::IBitmap* oshBitmap)
{
	if(_graphIsRunning || oshBitmap == NULL)
		SetAlphaBitmap(oshBitmap);
}

STDMETHODIMP OSHMixerPlugin::SetSyncSource(/* [in] */ IReferenceClock* pClock)
{
	CComPtr<IUnknown> ptr = pClock;
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::SetSyncSource([0x%08X], clock [0x%08X]) : FilterGraph=0x%08X\r\n",
		this, static_cast<IUnknown*>(ptr), _filterGraph);

	return S_OK;
}

//
// IDistributorNotify
//
STDMETHODIMP OSHMixerPlugin::NotifyGraphChange()
{
	if(_insideNotifyGraphChange)
		return S_OK;

	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::NotifyGraphChange([0x%08X]) : FilterGraph=0x%08X\r\n",
		this, _filterGraph);

	if(_filterGraph == NULL)
		return S_OK;

	_insideNotifyGraphChange = true;

	DirectShowUtility::DumpFilterGraph(_filterGraph);
	HRESULT hr = _vmrStateController.NotifyGraphChange(_filterGraph);

	EnsureVMRMode(_filterGraph);
	
	_insideNotifyGraphChange = false;

	return S_OK;
}

STDMETHODIMP OSHMixerPlugin::Pause()
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
		"OSHMixerPlugin::Pause([0x%08X])\r\n", this);

	if(_insidePause) return S_OK;
	_insidePause = true;

	if(!_graphIsRunning)
	{
		HRESULT hr = RemoveFilters();

		hr = InsertFilters();

		//hr = RemoveUnconnectedInsertedFilters(_filterGraph);

		//CComPtr<IReferenceClock> clock;
		//HRESULT hr = DirectShowUtility::FindFilterById(_filterGraph, CLSID_DSoundRender, &clock);
		//if(hr == S_OK)
		//{
		//	CComPtr<IMediaFilter> mediaFilter;
		//	hr = _filterGraph->QueryInterface(&mediaFilter);
		//	if(SUCCEEDED(hr))
		//		hr = mediaFilter->SetSyncSource(clock);

		//	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		//		"OSHMixerPlugin::Pause([0x%08X]) : setting reference clock - 0x%08X\r\n", this, hr);
		//}
	}
	_graphIsRunning = false;

	_insidePause = false;

	return S_OK;
}

STDMETHODIMP OSHMixerPlugin::Run(REFERENCE_TIME tStart)
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
		"OSHMixerPlugin::Run([0x%08X])\r\n", this);

	_graphIsRunning = true;

	if(_filterGraph == NULL)
		return S_OK;

	if(_oshSourceConnector)
	{
		if(_oshSourceConnector->IsVisible())
		{
			CComPtr<OSHLibrary::IBitmap> oshBitmap;
			HRESULT hr = _oshSourceConnector->GetOSHBitmap(&oshBitmap);
			if(SUCCEEDED(hr))
				Refresh(oshBitmap);
		}
	}

	return S_OK;
}

STDMETHODIMP OSHMixerPlugin::Stop()
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
		"OSHMixerPlugin::Stop([0x%08X])\r\n", this);

	if(_insideStop) return S_OK;
	_insideStop = true;

	_graphIsRunning = false;

	if(_filterGraph != NULL)
	{
		if(_insertedFilters.GetSize() > 0 || _removedFilters.GetSize() > 0)
		{
			// Stops the graph to be able to remove the filters.
			CComPtr<IMediaControl> mediaControl;
			HRESULT hr = _filterGraph->QueryInterface(&mediaControl);
			if(SUCCEEDED(hr))
				hr = mediaControl->Stop();
		}

		if(_insertedFilters.GetSize() > 0)
		{
			ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
				"OSHMixerPlugin::Stop([0x%08X]) : rolling back %d inserted filter(s)...\r\n", this, _insertedFilters.GetSize());

			HRESULT hr = RemoveInsertedFilters();
		}

		if(_removedFilters.GetSize() > 0)
		{
			ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
				"OSHMixerPlugin::Stop([0x%08X]) : rolling back %d removed filter(s)...\r\n", this, _removedFilters.GetSize());

			// Returning the removed filter back, otherwise the player might be confused trying to reconnect it.
			HRESULT hr = ReturnRemovedFilters();
		}
	}
	_insideStop = false;

	return S_OK;
}

/// <summary>
/// Removes filters specified in the configuaration file for the current application from the graph.
/// </summary>
HRESULT OSHMixerPlugin::RemoveFilters()
{
	if(_filterGraph == NULL) return E_UNEXPECTED;

	AppConfig& appConfig = Configuration::Current().GetCurrentAppConfig();
	int length = appConfig.RemoveFilters.GetSize();
	if(length == 0) return S_FALSE;

	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::RemoveFilters([0x%08X]) : removing %d filter(s)...\r\n", this, length);

	HRESULT hr = S_OK;
	for(int i = 0; i < length; i++)
	{
		GUID filterId = appConfig.RemoveFilters[i].ClassId;
		CComPtr<IBaseFilter> filter;
		hr = DirectShowUtility::FindFilterById(_filterGraph, filterId, &filter);
		if(hr == S_OK)
		{
			hr = DirectShowUtility::BypassFilter(_filterGraph, filter, true);
			if(hr == S_FALSE) continue;

			if(hr == S_OK)
				_removedFilters.Add(filter);
			
			ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
				"OSHMixerPlugin::RemoveFilters([0x%08X]) : filter %S - 0x%08X\r\n",
				this, DirectShowUtility::GuidToString(filterId).GetBuffer(), hr);
		}
		if(FAILED(hr)) break;
	}
	return hr;
}

HRESULT OSHMixerPlugin::ReturnRemovedFilters()
{
	if(_removedFilters.GetSize() == 0) return S_FALSE;
	if(_filterGraph == NULL) return E_UNEXPECTED;

	HRESULT result = S_OK;
	for(int i = _removedFilters.GetSize() - 1; i >= 0; i--)
	{
		CComPtr<IBaseFilter>& filter = _removedFilters[i];

		FILTER_INFO info;
		HRESULT hr = filter->QueryFilterInfo(&info);
		if(SUCCEEDED(hr))
			hr = _filterGraph->AddFilter(filter, info.achName);
		else
			hr =_filterGraph->AddFilter(filter, L"Ghost");

		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
			"OSHMixerPlugin::ReturnRemovedFilters([0x%08X]) : filter 0x%08X - 0x%08X\r\n",
			this, filter.p, hr);

		_removedFilters.RemoveAt(i);
		if(FAILED(hr)) result = hr;
	}
	return result;
}


/// <summary>
/// Inserts filters specified in the configuaration file for the current application to the graph.
/// </summary>
HRESULT OSHMixerPlugin::InsertFilters()
{
	if(_filterGraph == NULL) return E_UNEXPECTED;

	AppConfig& appConfig = Configuration::Current().GetCurrentAppConfig();
	int length = appConfig.InsertFilters.GetSize();
	if(length == 0) return S_FALSE;

	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::InsertFilters([0x%08X]) : inserting %d filter(s).\r\n", this, length);

	HRESULT hr = S_OK;
	for(int i = 0; i < length; i++)
	{
		InsertFilterCommand& command = appConfig.InsertFilters[i];
		
		hr = DirectShowUtility::InsertFilter(_filterGraph,
			command.ClassId, command.AfterFilterClassId, command.BeforeFilterClassId, _insertedFilters);
		if(FAILED(hr))
		{
			ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
				"OSHMixerPlugin::InsertFilters([0x%08X]) : cannot insert filter %S - 0x%08X\r\n",
				this, DirectShowUtility::GuidToString(command.ClassId), hr);
			break;
		}
	}
	return hr;
}

HRESULT OSHMixerPlugin::RemoveInsertedFilters()
{
	if(_insertedFilters.GetSize() == 0) return S_FALSE;
	if(_filterGraph == NULL) return E_UNEXPECTED;

	HRESULT result = S_OK;
	for(int i = _insertedFilters.GetSize() - 1; i >= 0; i--)
	{
		CComPtr<IBaseFilter>& filter = _insertedFilters[i];
		HRESULT hr = DirectShowUtility::BypassFilter(_filterGraph, filter, true);
		
		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
			"OSHMixerPlugin::RemoveInsertedFilters([0x%08X]) : filter 0x%08X - 0x%08X\r\n",
			this, filter.p, hr);

		_insertedFilters.RemoveAt(i);
		if(FAILED(hr)) result = hr;
	}
	return result;
}

HRESULT OSHMixerPlugin::RemoveUnconnectedInsertedFilters()
{
	if(_insertedFilters.GetSize() == 0) return S_FALSE;
	if(_filterGraph == NULL) return E_UNEXPECTED;

	bool removed = false;
	for(int i = _insertedFilters.GetSize() - 1; i >= 0; i--)
	{
		CComPtr<IBaseFilter> filter = _insertedFilters[i];
		CComPtr<IPin> pin;
		HRESULT hr = DirectShowUtility::FindUnconnectedPin(filter, PINDIR_INPUT, &pin);
		if(FAILED(hr)) return hr;
		if(hr != S_OK) return S_FALSE;

		pin.Release();
		hr = DirectShowUtility::FindUnconnectedPin(filter, PINDIR_OUTPUT, &pin);
		if(FAILED(hr)) return hr;
		if(hr != S_OK) return S_FALSE;

		hr = _filterGraph->RemoveFilter(filter);
		_insertedFilters.RemoveAt(i);
		removed = true;

		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
			"OSHMixerPlugin::RemoveUnconnectedInsertedFilters([0x%08X]) : filter 0x%08X - 0x%08X\r\n",
			this, filter.p, hr);

	}
	return removed ? S_OK : S_FALSE;
}

/// <summary>
/// Check that VMR is in Mixing mode.
/// </summary>
HRESULT OSHMixerPlugin::EnsureVMRMode(IFilterGraph* filterGraph)
{
	HRESULT hr = EnsureVMRMode<IVMRFilterConfig, CLSID_VideoMixingRenderer>(filterGraph);
	if(hr == S_FALSE)
		hr = EnsureVMRMode<IVMRFilterConfig9, CLSID_VideoMixingRenderer9>(filterGraph);
	return hr;
}

template<typename IVMRFilterConfigT, REFGUID VMRGUID>
HRESULT OSHMixerPlugin::EnsureVMRMode(IFilterGraph* filterGraph)
{
	if(filterGraph == NULL) return E_INVALIDARG;

	CComPtr<IVMRFilterConfigT> vmrFilterConfig;
	HRESULT hr = DirectShowUtility::FindFilterById(filterGraph, VMRGUID, &vmrFilterConfig);
	if(FAILED(hr) || hr == S_FALSE) return hr;

	CComPtr<IBaseFilter> vmrFilter;
	hr = vmrFilterConfig.QueryInterface(&vmrFilter);
	if(FAILED(hr)) return hr;

	DWORD numberOfStreams = 0;
	hr = vmrFilterConfig->GetNumberOfStreams(&numberOfStreams);
	if(hr == VFW_E_VMR_NOT_IN_MIXER_MODE)
	{
		AppConfig& appConfig = Configuration::Current().GetCurrentAppConfig();

		if(!appConfig.ForceMixingMode)
			return S_OK;

		if(!Configuration::Current().IsMultipleMixersAllowed())
		{
			hr = _vmrStateController.CanSetMixingMode();
			if(FAILED(hr) || hr == S_FALSE)
			{
				//hr = filterGraph->RemoveFilter(vmrFilter);
				ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
					"OSHMixerPlugin::EnsureVMRMode() : VMR mixing mode is not allowed - 0x%08X.\r\n", hr);
				
				return S_OK;
			}
		}

		// -----------------------
		//hr = vmrFilterConfig->SetRenderingPrefs(RenderPrefs_ForceOverlays);
		//if(FAILED(hr))
		//{
		//	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
		//		"OSHMixerPlugin::EnsureVMRMode() : Can't set rendering prefs - 0x%08X.\r\n", hr);
		//}
		// -----------------------

		// VMR is not in a mixer mode, so it has only one input pin.
		// Set number of streams to enable VMR mixer mode.
		numberOfStreams = appConfig.NumberOfStreams;
		hr = vmrFilterConfig->SetNumberOfStreams(numberOfStreams);
		if(hr == VFW_E_WRONG_STATE && appConfig.CanReconnect)
		{
			ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
				"OSHMixerPlugin::EnsureVMRMode() - Reconnecting VMR...\r\n", hr);

			// Number of streams can't be changed when VMR is connected.
			// Try to disconnect, set number of stream again and then reconnect pins.
			ConnectionInfoCollection connections;
			hr = DirectShowUtility::SaveConnections(vmrFilter, connections);
			if(SUCCEEDED(hr))
			{
				hr = DirectShowUtility::Disconnect(filterGraph, connections);
				if(SUCCEEDED(hr))
				{
					hr = vmrFilterConfig->SetNumberOfStreams(numberOfStreams);

					HRESULT hr2 = DirectShowUtility::RestoreConnections(filterGraph, connections);
					ATLASSERT(SUCCEEDED(hr2));
					if(SUCCEEDED(hr)) hr = hr2;
				}
			}
		}
		if(SUCCEEDED(hr))
		{
			if(!Configuration::Current().IsMultipleMixersAllowed())
				_vmrStateController.NotifyMixingModeSet(filterGraph, vmrFilter);
		}

		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
			"OSHMixerPlugin::EnsureVMRMode() : Setting number of streams - 0x%08X.\r\n", hr);
	}
	else
	{
		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Info,
			"OSHMixerPlugin::EnsureVMRMode() : VMR is in mixer mode with %d streams - 0x%08X.\r\n",
			numberOfStreams, hr);

		if(!Configuration::Current().IsMultipleMixersAllowed())
			_vmrStateController.NotifyMixingModeSet(filterGraph, vmrFilter);
	}

	return hr;
}


/// <summary>
/// Check that VMR is in Renderless mode.
/// </summary>
//HRESULT OSHMixerPlugin::EnsureVMRMode(IFilterGraph* filterGraph)
//{
//	CComPtr<IVMRFilterConfig> vmrFilterConfig;
//	HRESULT hr = DirectShowUtility::FindFilterById(filterGraph,
//		CLSID_VideoMixingRenderer, &vmrFilterConfig);
//	if(FAILED(hr) || hr == S_FALSE) return hr;
//
//	DWORD mode = 0;
//	hr = vmrFilterConfig->GetRenderingMode(&mode);
//	if(FAILED(hr)) return hr;
//
//	if(mode & VMRMode_Renderless)
//		return S_OK;
//
//	hr = vmrFilterConfig->SetRenderingMode(VMRMode_Renderless);
//	ATLASSERT(SUCCEEDED(hr));
//	if(SUCCEEDED(hr))
//	{
//		ATLASSERT(_oshAP == NULL);
//		if(_oshAP)
//		{
//			_oshAP->Release();
//			_oshAP = NULL;
//		}
//		CComPtr<IBaseFilter> vmrFilter;
//		vmrFilter = vmrFilterConfig;
//		//HRESULT hr = DirectShowUtility::FindFilterById(_filterGraph,
//		//	CLSID_VideoMixingRenderer, &vmrFilter);
//		if(hr == S_OK)
//		{
//			_oshAP = new AllocatorPresenter();
//			_oshAP->AddRef();
//			hr = _oshAP->Initialize(vmrFilter);
//			if(FAILED(hr))
//			{
//				_oshAP->Release();
//				ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
//					"OSHMixerPlugin::SetAlphaBitmap() : Cannot create custom AllocatorPresenter - 0x%08X\r\n", hr);
//			}
//		}
//	}
//	return hr;
//}

HRESULT SetAlphaBitmap2(IBaseFilter* vmrFilter)
{
	if(vmrFilter == NULL) return E_POINTER;

	//CComPtr<IDirect3D9> d3d;
	//d3d.Attach(::Direct3DCreate9(D3D_SDK_VERSION));
	//if(d3d == NULL)
	//{
	//	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Error,
	//		"OSHMixerPlugin::SetAlphaBitmap() : Can't create D3D - 0x%08X\r\n", hr);
	//	return E_FAIL;
	//}

	CComPtr<IVMRMonitorConfig> monitorConfig;
	HRESULT hr = vmrFilter->QueryInterface(&monitorConfig);
	if(FAILED(hr))
	{
		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Error,
			"OSHMixerPlugin::SetAlphaBitmap() : Can't get IVMRMonitorConfig interface - 0x%08X\r\n", hr);
		return hr;
	}

	VMRGUID monitorGuid;
	hr = monitorConfig->GetMonitor(&monitorGuid);
	if(FAILED(hr))
	{
		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Error,
			"OSHMixerPlugin::SetAlphaBitmap() : Can't get current monitor - 0x%08X\r\n", hr);
		return hr;
	}

	wchar_t monitorGuidString[100] = {0};
	StringFromGUID2(monitorGuid.GUID, monitorGuidString, SIZEOF_ARRAY(monitorGuidString));

	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Error,
		"OSHMixerPlugin::SetAlphaBitmap() : Monitor GUID = %S\r\n", monitorGuidString);

	return S_OK;

	//CComPtr<IVMRSurfaceAllocator> surfaceAllocator;
	//HRESULT hr = vmrFilter->QueryInterface(&surfaceAllocator);
	//if(FAILED(hr))
	//{
	//	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Error,
	//		"OSHMixerPlugin::SetAlphaBitmap() : Can't get IVMRSurfaceAllocator interface - 0x%08X\r\n", hr);
	//	return hr;
	//}

	//VMRALLOCATIONINFO allocationInfo = {0};
	//DWORD numberOfBuffers = 1;
	//CComPtr<IDirectDrawSurface7
	//LPDIRECTDRAWSURFACE7 surface;
	//surfaceAllocator->AllocateSurface(0, &allocationInfo, &numberOfBuffers, &surface);
	//surface->
	//IVMRMixerBitmap,
}


HRESULT OSHMixerPlugin::SetAlphaBitmap(OSHLibrary::IBitmap* bitmap)
{
	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::SetAlphaBitmap(0x%08X)\r\n", bitmap);

	HRESULT hr = S_OK;

	WTL::CDC sourceDC;
	WTL::CBitmap sourceBitmap;
	CSize sourceBitmapSize;
	COLORREF transparentColor(0);

	// Create DC and Bitmap
	if(bitmap != NULL)
	{
		BYTE* bitmapPixels = NULL;
		BITMAPINFO* bitmapInfo = NULL;

		hr = GetBitmapInfo(bitmap, &bitmapInfo, &bitmapPixels);
		if(FAILED(hr)) return hr;

		if(bitmapPixels == NULL || bitmapInfo == NULL)
			return E_FAIL;

		CDC screenDC(::GetDC(NULL));
		sourceDC.CreateCompatibleDC(screenDC);

		HBITMAP hSourceBitmap;
		hr = this->CreateBitmap(screenDC, bitmapInfo, bitmapPixels, &hSourceBitmap);
		if(FAILED(hr)) return hr;

		sourceBitmap.Attach(hSourceBitmap);
		sourceDC.SelectBitmap(sourceBitmap);

		sourceBitmap.GetSize(sourceBitmapSize);

		transparentColor = FindTransparentColor(bitmapPixels, bitmapInfo->bmiHeader.biSizeImage);
		ATLTRACE_TS(atlTraceOSHSourcePin, TraceLevel::Verbose,
			"OSHSourcePin::SetAlphaBitmap() : Using transparent color 0x%08X\r\n", transparentColor);
	}

	hr = SetAlphaBitmap<IVMRMixerBitmap, VMRALPHABITMAP, CLSID_VideoMixingRenderer>(
		_filterGraph, sourceDC, sourceBitmapSize, transparentColor);
	if(FAILED(hr))
	{
		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
			"OSHMixerPlugin::SetAlphaBitmap() : Using VMR7 - 0x%08X\r\n", hr);
		return hr;
	}
	if(hr == S_OK) return hr;

	hr = SetAlphaBitmap<IVMRMixerBitmap9, VMR9AlphaBitmap, CLSID_VideoMixingRenderer9>(
		_filterGraph, sourceDC, sourceBitmapSize, transparentColor);
	if(FAILED(hr))
	{
		ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
			"OSHMixerPlugin::SetAlphaBitmap() : Using VMR9 - 0x%08X\r\n", hr);
		return hr;
	}
	if(hr == S_OK) return hr;

	ATLTRACE_TS(atlTraceOSHMixerPlugin, TraceLevel::Verbose,
		"OSHMixerPlugin::SetAlphaBitmap() : Can't get VMR7 or VMR9 - 0x%08X\r\n", hr);

	return hr;
}

template<typename IVMRMixerBitmapT, typename VMRAlphaBitmapT, REFGUID VMRGUID>
HRESULT OSHMixerPlugin::SetAlphaBitmap(IFilterGraph* filterGraph,
	HDC hdc, CSize bitmapSize, COLORREF transparentColor)
{
	CComPtr<IVMRMixerBitmapT> mixerBitmap;
	HRESULT hr = DirectShowUtility::FindFilterById(filterGraph, VMRGUID, &mixerBitmap);
	if(FAILED(hr) || mixerBitmap == NULL) return S_FALSE;

	VMRAlphaBitmapT bmpInfo;
	ZeroMemory(&bmpInfo, sizeof(bmpInfo));

	bmpInfo.dwFlags = (hdc == NULL ? VMRBITMAP_DISABLE : VMRBITMAP_HDC | VMRBITMAP_SRCCOLORKEY);
	bmpInfo.hdc = hdc;

	// Show the entire bitmap in the top-left corner of the video image.
	SetRect(&bmpInfo.rSrc, 0, 0, bitmapSize.cx, bitmapSize.cy);
	bmpInfo.rDest.left = 0.0f;
	bmpInfo.rDest.top = 0.0f;
	bmpInfo.rDest.right = (float)1.0;
	bmpInfo.rDest.bottom = (float)1.0;

	// Set the transparency value (1.0 is opaque, 0.0 is transparent).
	bmpInfo.fAlpha = 1.0f;
	
	bmpInfo.clrSrcKey = transparentColor;

	hr = mixerBitmap->SetAlphaBitmap(&bmpInfo);
	return hr;
}

/// <summary>
/// This method scans passed bitmap pixels looking for completely transparent pixel
/// and returns its color.
/// </summary>
COLORREF OSHMixerPlugin::FindTransparentColor(BYTE* bitmapPixels, size_t bitmapSize)
{
	DWORD* dwBitmapPixels = (DWORD*)bitmapPixels;
	for(size_t i = 0; i < (bitmapSize / 4); i++)
	{
		if((*dwBitmapPixels & 0xFF000000) == 0x00000000)
			return (COLORREF)*dwBitmapPixels;
		dwBitmapPixels++;
	}
	return DefaultTransparentColor;
}

/// <summary>
/// </summary>
/// <param name="bitmapInfo">Pointer to the bitmap header.</param>
/// <param name="bitmapPixels">Points to pixel bits.</param>
HRESULT OSHMixerPlugin::GetBitmapInfo(OSHLibrary::IBitmap* oshBitmap, BITMAPINFO** bitmapInfo, BYTE** bitmapPixels)
{
	LONG bitmapDataPtr;
	HRESULT hr = oshBitmap->get_BitmapInfo(&bitmapDataPtr);
	if(FAILED(hr))
	{
		ATLTRACE_TS(atlTraceOSHSourcePin, TraceLevel::Verbose,
			"OSHSourcePin::GetBitmapInfo() : 0x%08X\r\n", hr);
		return hr;
	}

	BYTE* bitmapData = (BYTE*)(UINT_PTR)bitmapDataPtr;

	BITMAPINFOHEADER* bitmapInfoHeader = reinterpret_cast<BITMAPINFOHEADER*>(bitmapData);

	if(bitmapInfoHeader->biSize != sizeof(BITMAPINFOHEADER))
	{
		ATLASSERT(bitmapInfoHeader->biSize != sizeof(BITMAPINFOHEADER));
		return E_FAIL;
	}

	// Store a pointer to the BITMAPINFO
	if(bitmapInfo != NULL)
		*bitmapInfo = reinterpret_cast<BITMAPINFO*>(bitmapInfoHeader);

	// Store a pointer to the starting address of the pixel bits
	if(bitmapPixels != NULL)
		*bitmapPixels = bitmapData + bitmapInfoHeader->biSize;

	return hr;
}

HRESULT OSHMixerPlugin::CreateBitmap(HDC hdc, IN BITMAPINFO* bitmapInfo, IN BYTE* bitmapPixels, OUT HBITMAP* hBitmap)
{
	*hBitmap = NULL;

	// Get bitmap info.
	*hBitmap = ::CreateCompatibleBitmap(hdc, bitmapInfo->bmiHeader.biWidth, bitmapInfo->bmiHeader.biHeight);
	if(*hBitmap == NULL)
		return AtlHresultFromLastError();

	int result = ::SetDIBits(hdc, *hBitmap, 0, bitmapInfo->bmiHeader.biHeight,
		bitmapPixels, bitmapInfo, DIB_RGB_COLORS);
	if(result == 0)
		return AtlHresultFromLastError();

	return S_OK;
}


/*
HRESULT OSHMixerPlugin::GetVMRPinToConnect(IFilterGraph* filterGraph, IPin** pin)
{
	if(pin == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	*pin = NULL;

	CComPtr<IBaseFilter> vmrFilter;
	hr = FindVMRFilter(filterGraph, &vmrFilter);
	if(FAILED(hr)) return hr;
	if(hr == S_FALSE) return E_FAIL;

	CComPtr<IVMRFilterConfig> vmrConfig;
	hr = vmrFilter->QueryInterface(&vmrConfig);
	if(FAILED(hr)) return hr;

	// Find an unconnected pin.
	DWORD numberOfPins = 0;
	DWORD numberOfConnectedPins = 0;
	hr = DirectShowUtility::GetNumberOfPins(vmrFilter, &numberOfPins, &numberOfConnectedPins);
	if(FAILED(hr)) return hr;

	// If there is no an unconnected pin then create one.
	if((numberOfPins == 1) || 
		(numberOfPins == numberOfConnectedPins && numberOfPins < MAX_MIXER_STREAMS))
	{
		ConnectionInfoCollection connections;
		hr = SaveConnections(vmrFilter, connections);
		if(SUCCEEDED(hr))
		{
			hr = Disconnect(filterGraph, connections);
			if(SUCCEEDED(hr))
			{
				hr = vmrConfig->SetNumberOfStreams((DWORD)min(numberOfPins + 1, MAX_MIXER_STREAMS));

				HRESULT hr2 = RestoreConnections(filterGraph, connections);
				ATLASSERT(SUCCEEDED(hr2));
				if(SUCCEEDED(hr)) hr = hr2;
			}
		}
	}

	if(FAILED(hr)) return hr;

	hr = DirectShowUtility::GetNumberOfPins(vmrFilter, &numberOfPins, &numberOfConnectedPins);
	if(FAILED(hr)) return hr;

	if(numberOfConnectedPins == 0 || numberOfPins == numberOfConnectedPins)
		return E_FAIL;

	// Find an unconnected VMR input pin.
	CComPtr<IPin> vmrInPin;
	hr = DirectShowUtility::FindUnconnectedPin(vmrFilter, PINDIR_INPUT, &vmrInPin);
	if(FAILED(hr)) return hr;

	(*pin) = vmrInPin;
	(*pin)->AddRef();

	//DWORD dwMaxStreams = 0;
	//hr = vmrConfig->GetNumberOfStreams(&dwMaxStreams);
	//if(FAILED(hr) && hr != VFW_E_VMR_NOT_IN_MIXER_MODE) return hr;
	//if(hr == VFW_E_VMR_NOT_IN_MIXER_MODE)
	//	dwMaxStreams = 1;
	//VFW_E_WRONG_STATE

	return hr;
}
*/

HRESULT OSHMixerPlugin::FindVMRFilter(IFilterGraph* filterGraph, IBaseFilter** vmrFilter)
{
	if(vmrFilter == NULL)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	*vmrFilter = NULL;

	hr = DirectShowUtility::FindFilterById(filterGraph, CLSID_VideoMixingRenderer, vmrFilter);
	if(FAILED(hr)) return hr;
	if(hr == S_OK) return hr;

	hr = DirectShowUtility::FindFilterById(filterGraph, CLSID_VideoMixingRenderer9, vmrFilter);
	if(FAILED(hr)) return hr;
	if(hr == S_OK) return hr;

	return S_FALSE;
}

